package server

import (
	"fmt"
	"github.com/golang/protobuf/proto"
	"log"
	"math"
	"server5/Dao"
	"server5/Interface"
	"server5/protobuf"
	"server5/utils"
	"strconv"
	"strings"
	"time"
)

type GameRoom struct {
	//玩家列表
	players map[Interface.IUser]Interface.IPlayer
	//据点列表
	strongholds map[int32]Interface.IStronghold
	//单位列表
	units map[int32]Interface.IUnit
	//购买单位时使用的唯一单位ID
	unitId int32	//加锁
	//开始游戏的时间
	startTime time.Time
	//总定时器
	tick *time.Ticker
	//定时器触发的次数
	tickNum int32
	//游戏结束通知关闭定时器
	stopChan chan bool
	//客户端来的请求
	requestChan chan Interface.IRequest
}

func (g *GameRoom) Units() map[int32]Interface.IUnit {
	return g.units
}

func (g *GameRoom) RequestChan() chan Interface.IRequest {
	return g.requestChan
}

func NewGameRoom(c1,c2,c3,c4 Interface.IUser) *GameRoom {
	gameRoom := &GameRoom{
		players: make(map[Interface.IUser]Interface.IPlayer),
		strongholds: make(map[int32]Interface.IStronghold),
		units: make(map[int32]Interface.IUnit),
		unitId: 1,
		tick: time.NewTicker(time.Millisecond * 10),
		tickNum: 0,
		stopChan: make(chan bool, 1),
		requestChan: make(chan Interface.IRequest, 1024),
	}

	gameRoom.players[c1] = NewPlayer(c1,1,2,gameRoom)
	gameRoom.players[c2] = NewPlayer(c2,2,1,gameRoom)
	gameRoom.players[c3] = NewPlayer(c3,3,4,gameRoom)
	gameRoom.players[c4] = NewPlayer(c4,4,3,gameRoom)

	//城堡
	gameRoom.strongholds[1] = NewCastle(1,gameRoom.players[c1],gameRoom)
	gameRoom.strongholds[2] = NewCastle(2,gameRoom.players[c2],gameRoom)
	gameRoom.strongholds[3] = NewCastle(3,gameRoom.players[c3],gameRoom)
	gameRoom.strongholds[4] = NewCastle(4,gameRoom.players[c4],gameRoom)

	//村庄
	gameRoom.strongholds[5] = NewVillage(5,nil,gameRoom)
	gameRoom.strongholds[6] = NewVillage(6,nil,gameRoom)
	gameRoom.strongholds[9] = NewVillage(9,nil,gameRoom)
	gameRoom.strongholds[10] = NewVillage(10,nil,gameRoom)

	//魔法塔
	gameRoom.strongholds[7] = NewMagicTower(7,nil,gameRoom)
	gameRoom.strongholds[8] = NewMagicTower(8,nil,gameRoom)


	gameRoom.players[c1].AddStronghold(gameRoom.strongholds[1])
	gameRoom.players[c2].AddStronghold(gameRoom.strongholds[2])
	gameRoom.players[c3].AddStronghold(gameRoom.strongholds[3])
	gameRoom.players[c4].AddStronghold(gameRoom.strongholds[4])


	gameRoom.players[c1].ReCalculate()
	gameRoom.players[c2].ReCalculate()
	gameRoom.players[c3].ReCalculate()
	gameRoom.players[c4].ReCalculate()

	c1.SetGameRoom(gameRoom)
	c2.SetGameRoom(gameRoom)
	c3.SetGameRoom(gameRoom)
	c4.SetGameRoom(gameRoom)

	c1.SetStatus(utils.Gaming)
	c2.SetStatus(utils.Gaming)
	c3.SetStatus(utils.Gaming)
	c4.SetStatus(utils.Gaming)
	return gameRoom
}


func (g *GameRoom) StartGame() {

	for _, p := range g.players {
		g.InitGame(p)
	}

	log.Println("开始一局游戏")
	//Debug
	fmt.Println("开始一局游戏")

	g.startTime = time.Now()

	for {
		select {
		case request := <- g.requestChan:
			switch request.GetMsgType() {
			case protobuf.MsgType_MoveUnit:
				g.MoveUnit(request)
			case protobuf.MsgType_AttackUnit:
				g.AttackUnit(request)
			case protobuf.MsgType_BuyUnit:
				g.BuyUnit(request)
			case protobuf.MsgType_OccupyStronghold:
				g.OccupyStronghold(request)
			case protobuf.MsgType_CancelOccupyStronghold:
				g.CancelOccupyStronghold(request)
			case protobuf.MsgType_UpStronghold:
				g.UpStronghold(request)
			case protobuf.MsgType_SkillInfo:
				g.SkillUnit(request)
			case protobuf.MsgType_UpdateLand:
				g.UpdateLand(request)
			default:
				log.Println("无法识别的消息类型，没有对应的处理方法")
				fmt.Println("无法识别的消息类型，没有对应的处理方法")
			}
		case <- g.tick.C:
			g.tickNum++
			//更新金币
			if g.tickNum % 100 == 0 {
				g.TickWork()
			}
			//更新移动
			for _, u := range g.units {
				u.Move()
			}
			//更新攻击
			for _, u := range g.units {
				u.Dead()
				u.Attack()
			}
			//更新占领建筑物
			for _, s := range g.strongholds {
				if s.Occupy() {
					g.VictoryOrDefeat()
				}
 			}
		case <-g.stopChan:
			g.tick.Stop()
			return
		}
	}
}

//每秒钟更新玩家金币，魔法，人口信息
func (g *GameRoom) TickWork () {
	var tickerMsg protobuf.TickerMsg
	for c,p := range g.players{
		p.AddMoney(p.MoneyAdd())
		p.AddMagic(p.MagicAdd())
		tickerMsg.Money = p.Money()
		tickerMsg.Magic = p.Magic()
		tickerMsg.MaxPop = p.MaxPop()
		tickerMsg.NowPop = p.Pop()

		c.Connection().SendMsg(protobuf.MsgType_Ticker,&tickerMsg)
	}
	g.tickNum = 0
}


//初始化游戏
func (g *GameRoom) InitGame(player Interface.IPlayer) {
	var initGameMsg protobuf.InitGameMsg
	initGameMsg.PlayerId = player.PlayerId()
	player.User().Connection().SendMsg(protobuf.MsgType_InitGame,&initGameMsg)
}

//广播消息
func (g *GameRoom) Broadcast(msgType protobuf.MsgType,message proto.Message) {
	for c,_ := range g.players{
		c.Connection().SendMsg(msgType,message)
	}
}


//买兵
func (g *GameRoom) BuyUnit(request Interface.IRequest){
	var buyUnitMsg protobuf.BuyUnitMsg
	proto.Unmarshal(request.GetData(),&buyUnitMsg)

	//校验数据
	s, ok := g.strongholds[buyUnitMsg.StrongholdId]
	if !ok {
		fmt.Println("没有这个据点")
		return
	}
	if s.Player() == nil || s.Player().PlayerId() != buyUnitMsg.PlayerId {
		fmt.Println("数据错误，该玩家企图在不属于自己的据点买兵")
		return
	}

	if s.BuyUnit(g.unitId,buyUnitMsg.UnitType) {
		fmt.Printf("%d号玩家买了一个%d号兵种，编号为%d\n",buyUnitMsg.PlayerId,buyUnitMsg.UnitType,g.unitId)
		g.unitId++
	}
}

func (g *GameRoom) MoveUnit(request Interface.IRequest){

	var moveUnitMsg protobuf.MoveUnitMsg
	proto.Unmarshal(request.GetData(),&moveUnitMsg)

	u ,ok := g.units[moveUnitMsg.UnitId]
	if !ok {
		fmt.Println("移动出错，不存在该单位")
		return
	}

	//战斗和追赶状态下不能移动
	if u.Status() == utils.Fighting || u.Status() == utils.Chase {
		return
	}

	s := moveUnitMsg.Roads

	k := 0
	c := strings.Split(s," ")

	n := len(c) / 2
	m := 2
	//动态创建二维数组
	grid := make([][]int32, n)
	for i := 0; i < n; i++ {
		grid[i] = make([]int32, m)
	}
	for i := 0 ;i < len(c) - 1 ;i++  {
		k0,_ := strconv.Atoi(c[i])
		grid[k][0] = int32(k0)
		i++
		k1,_ := strconv.Atoi(c[i])
		grid[k][1] = int32(k1)
		k++
	}

	//TODO 判断路径上的点能不能走
	if !GetMap().IsCanWork(grid) {
		//Debug
		fmt.Println("路径上的点有些不能走")
		return
	}

	if u.Status() == utils.Static || u.Status() == utils.Moving {
		u.SetStatus(utils.Moving)
	} else if u.Status() == utils.Attacked {
		u.SetStatus(utils.Chase)
	}
	u.SetPath(grid)
}

func (g *GameRoom) AttackUnit(request Interface.IRequest) {
	var attackUnitMsg protobuf.AttackUnitMsg
	proto.Unmarshal(request.GetData(),&attackUnitMsg)

	//Debug	攻击者 And 被攻击者
	fmt.Println(attackUnitMsg.UnitIdAttack,attackUnitMsg.UnitIdAttacked)

	attackUnit, ok := g.units[attackUnitMsg.UnitIdAttack]
	if !ok {
		fmt.Println("战斗出错，不存在该单位")
		return
	}

	attackedUnit, ok := g.units[attackUnitMsg.UnitIdAttacked]
	if !ok {
		fmt.Println("战斗出错，不存在该单位")
		return
	}

	//判断攻击距离
	if !attackUnit.IsInAttackRange(attackedUnit) {
		//Debug
		fmt.Println("不在攻击范围之内")
		return
	}

	if attackUnit.AttackTarget() == nil {
		attackUnit.SetAttackTarget(attackedUnit)
	}
	attackUnit.SetStatus(utils.Fighting)
}

//据点升级
func (g *GameRoom) UpStronghold(request Interface.IRequest) {
	var upStrongholdMsg protobuf.UpStrongholdMsg
	proto.Unmarshal(request.GetData(),&upStrongholdMsg)

	s,ok := g.strongholds[upStrongholdMsg.StrongholdId]
	if !ok {
		fmt.Println("数据错误，企图升级不存在的据点")
		return
	}

	if s.Level() != upStrongholdMsg.Level {
		fmt.Println("客户端城堡等级不一致")
		return
	}

	if s.UpLevel() {
		s.Player().ReSkillLevel()
		upStrongholdMsg.Level++
		g.Broadcast(protobuf.MsgType_UpStronghold,&upStrongholdMsg)
	}
}

//占领据点
func (g *GameRoom) OccupyStronghold(request Interface.IRequest){
	var occupyStrongholdMsg protobuf.OccupyStrongholdMsg
	proto.Unmarshal(request.GetData(),&occupyStrongholdMsg)

	//TODO 位置判断

	s,ok := g.strongholds[occupyStrongholdMsg.StrongholdId]
	if !ok {
		fmt.Println("数据错误，不存在的据点")
		return
	}
	u,ok := g.units[occupyStrongholdMsg.UnitId]
	if !ok {
		fmt.Println("数据错误，不存在的单位")
		return
	}

	if s.Status() == utils.Occupy {
		//Debug
		fmt.Println(s.Id(), "此据点正在被别人占领")
		return
	}

	s.SetStatus(utils.Occupy)
	s.SetOccupyUnitId(occupyStrongholdMsg.UnitId)

	player := u.Player()

	//中立建筑
	if s.Player() == nil {
		//if s.OccupyPlayer() == nil {
		//	//还没人占领，直接占领即可
		//} else if s.OccupyPlayer().PlayerId() == player.PlayerId() || s.OccupyPlayer().PlayerId() == player.FriendId() {
		//	//上次是友军占领，直接接着占领即可
		//} else {
		//	//上次是敌方占领，读条归零，再占领
		//	s.SetBar(0)
		//}
		if s.OccupyPlayer() != nil && s.OccupyPlayer().PlayerId() != player.PlayerId() && s.OccupyPlayer().PlayerId() != player.FriendId() {
			//上次是敌方占领，读条归零，再占领
			s.SetBar(0)
			s.SetStatus(utils.Occupy)

			var occupyStrongholdResultMsg protobuf.OccupyStrongholdResultMsg
			occupyStrongholdResultMsg.UnitId = occupyStrongholdMsg.UnitId
			occupyStrongholdResultMsg.StrongholdId = s.Id()
			occupyStrongholdResultMsg.Bar = 0
			occupyStrongholdResultMsg.Success = false
			g.Broadcast(protobuf.MsgType_OccupyStrongholdResult, &occupyStrongholdResultMsg)
		}
	} else if s.Player().PlayerId() != player.PlayerId() && s.Player().FriendId() != player.PlayerId() {
		//对方的据点，直接占领
	} else if s.Player().PlayerId() == player.PlayerId() || s.Player().FriendId() == player.PlayerId(){
		//友方的据点
		s.SetBar(0)
		s.SetStatus(utils.Normal)
		var occupyStrongholdResultMsg protobuf.OccupyStrongholdResultMsg
		occupyStrongholdResultMsg.UnitId = occupyStrongholdMsg.UnitId
		occupyStrongholdResultMsg.StrongholdId = s.Id()
		occupyStrongholdResultMsg.Bar = 0
		occupyStrongholdResultMsg.Success = false
		g.Broadcast(protobuf.MsgType_OccupyStrongholdResult, &occupyStrongholdResultMsg)
	}
	s.SetOccupyPlayer(player)
}

//判断是胜利还是失败，是否游戏结束
func (g *GameRoom) VictoryOrDefeat() {
	//结果为true表示占领据点成功，判断一次胜负
	var (
		red  = 0
		blue = 0
	)
	for _, p := range g.players {
		if p.PlayerId() == 1 || p.PlayerId() == 2 {
			fmt.Println(p.PlayerId(), "据点数量", p.GetStrongholdNum())
			red += p.GetStrongholdNum()
		} else {
			fmt.Println(p.PlayerId(), "据点数量", p.GetStrongholdNum())
			blue += p.GetStrongholdNum()
		}
	}
	if red == 0 {
		g.GameOver(false)
	} else if blue == 0 {
		g.GameOver(true)
	} else {
		fmt.Printf("12据点数为%d，34据点数为%d\n", red, blue)
	}
}

//true 表示12获胜，false表示34获胜
func (g *GameRoom) GameOver (RedIsWin bool){
	g.stopChan <- true
	//游戏时间，四舍五入
	t := int(math.Round(time.Now().Sub(g.startTime).Minutes()))

	fmt.Println("游戏结束，时间：",t)
	if t < 10 {
		t = 10
	}

	var gameOver protobuf.GameOverMsg
	for _,p := range g.players {

		if p.PlayerId() == 1 || p.PlayerId() == 2 {
			exp := g.Exp(t,RedIsWin)
			gold := g.Gold(t,RedIsWin)
			nextExp, addSp := p.User().AddExp(exp)

			p.User().AddGold(gold)

			gameOver.AddExp = int32(exp)
			gameOver.NextExp = int32(nextExp)
			gameOver.AddSp = int32(addSp)
			gameOver.Lv = int32(p.User().UserInfo().UserLv)
			gameOver.Exp = int32(p.User().UserInfo().UserExp)
			gameOver.AddGold = int32(gold)
			gameOver.Win = RedIsWin
		} else {
			exp := g.Exp(t,!RedIsWin)
			gold := g.Gold(t,!RedIsWin)
			nextExp, addSp := p.User().AddExp(exp)

			p.User().AddGold(gold)

			gameOver.AddExp = int32(exp)
			gameOver.NextExp = int32(nextExp)
			gameOver.AddSp = int32(addSp)
			gameOver.Lv = int32(p.User().UserInfo().UserLv)
			gameOver.Exp = int32(p.User().UserInfo().UserExp)
			gameOver.AddGold = int32(gold)
			gameOver.Win = !RedIsWin
		}
		Dao.GetUserDao().UpdateUser(p.User().UserInfo())
		p.User().Connection().SendMsg(protobuf.MsgType_GameOver,&gameOver)
	}

	for _,p := range g.players {
		p.User().SetStatus(utils.Online)
		p.User().SetGameRoom(nil)
	}

}

//游戏结束计算加多少经验
func (g *GameRoom) Exp(t int, isWin bool) int {
	if isWin {
		return 20 + t * 3
	}
	return 18 + t * 2
}

//游戏结束计算加多少金币
func (g *GameRoom) Gold(t int, isWin bool) int {
	if isWin {
		return 15 + t * 4
	}
	return 7 + t * 2
}


//取消占领据点
func (g *GameRoom) CancelOccupyStronghold(request Interface.IRequest){
	var CancelOccupyStrongholdMsg protobuf.CancelOccupyStrongholdMsg
	proto.Unmarshal(request.GetData(),&CancelOccupyStrongholdMsg)

	//TODO 位置判断
	s,ok := g.strongholds[CancelOccupyStrongholdMsg.StrongholdId]
	if !ok {
		fmt.Println("数据错误，不存在的据点")
		return
	}
	s.CancelOccupy(CancelOccupyStrongholdMsg.UnitId)
}


//技能
func (g *GameRoom) SkillUnit(request Interface.IRequest) {
	var skillInfo protobuf.SkillInfoMsg
	proto.Unmarshal(request.GetData(),&skillInfo)

	if g.players[request.GetConnection().User()].GetStrongholdNum() == 0 {
		fmt.Println("玩家没有据点，不能释放技能")
		return
	}

	switch skillInfo.SkillType {
	case utils.Damage:
		if g.players[request.GetConnection().User()].Damage(skillInfo.UnitIds) {
			g.Broadcast(protobuf.MsgType_SkillInfo,&skillInfo)
		}
	case utils.ReplyHp:
		if g.players[request.GetConnection().User()].ReplyHp() {
			g.Broadcast(protobuf.MsgType_SkillInfo,&skillInfo)
		}
	case utils.CallBoss:
		StrongholdId, _ := strconv.ParseInt(skillInfo.UnitIds,10,32)// 此时skillInfo.UnitIds存的是StrongholdId
		s,ok := g.strongholds[int32(StrongholdId)]
		if !ok {
			fmt.Println("数据错误，不存在的据点")
			return
		}
		if s.BuyUnit(g.unitId,utils.Boss){
			fmt.Printf("%d号玩家召唤了一个%d号的BOSS，编号为%d\n",g.players[request.GetConnection().User()].PlayerId(),utils.Boss,g.unitId)
			g.unitId++
			g.Broadcast(protobuf.MsgType_SkillInfo,&skillInfo)
		}
	}
}


//更新空地
func (g *GameRoom) UpdateLand(request Interface.IRequest) {
	var updateLandMsg protobuf.UpdateLandMsg
	proto.Unmarshal(request.GetData(),&updateLandMsg)

	fmt.Printf("%d号据点%d号空地 要建造%d号建筑\n",updateLandMsg.Stronghold,updateLandMsg.LandId,updateLandMsg.LandType)

	s, ok := g.strongholds[updateLandMsg.Stronghold]
	if !ok {
		//Debug
		fmt.Println("不存在的据点")
		return
	}
	//丢给据点去更新空地
	s.UpdateLand(updateLandMsg.LandId,updateLandMsg.LandType)
}


func (g *GameRoom) GetUnit(unitId int32) Interface.IUnit{
	return g.units[unitId]
}

func (g *GameRoom) AddUnit(unit Interface.IUnit) {
	g.units[unit.Id()] = unit
}

func (g *GameRoom) RemoveUnit(unitId int32) {
	delete(g.units,unitId)
}

func (g *GameRoom) Player(user Interface.IUser) Interface.IPlayer {
	return g.players[user]
}
